"""
Implements main window of VN Trader.
"""

import webbrowser
from functools import partial
from importlib import import_module
from typing import Callable
import pyqtgraph as pg
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import pyqtSignal, QTimer

from vnpy.event import EventEngine
from vnpy.event.event import *
from vnpy.trader.engine import MainEngine
from vnpy.trader.utility import get_icon_path, TRADER_DIR
from .editor import FindDialog as  CodeEditor
from .widget import (
    TickMonitor,
    PositionMonitor,
    AccountWidget,
    ActiveOrderMonitor,
    ConnectDialog,
    ContractManager,
    TradingWidget,
    AboutDialog,
    GlobalDialog,
    QuoteList,
    StatusCell, StatusComboBox, OrderPad
)

from vnpy.bq.chart.widget import TaWidget
from vnpy.bq.constant import period_zh_en_dict


class MainWindow(QtWidgets.QMainWindow):
    """
    Main window of VN Trader.
    """
    instrument_changed = pyqtSignal(str)  # 当主窗口中行情列表中的标的改变时触发该信号
    period_changed_signal = pyqtSignal(str)

    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super(MainWindow, self).__init__()
        self.statusbar = QtWidgets.QStatusBar(self)
        self.setStatusBar(self.statusbar)
        self.main_engine = main_engine
        self.event_engine = event_engine

        self.window_title = f"BQ [{TRADER_DIR}]宝量策略交易系统"

        self.connect_dialogs = {}
        self.widgets = {}
        self.current_instrument_id = ''
        self.exchange = None
        self.account_widget = None
        self.acc_timer = QTimer()

        self.init_ui()

    def init_ui(self):
        """"""
        self.setWindowTitle(self.window_title)

        self.init_dock()
        self.init_toolbar()
        self.init_menu()
        self.init_statusbar()
        # self.load_window_setting("custom")

    def init_statusbar(self):
        tee = self.event_engine
        status_item = {
            "td_connected": {"label": "交易连接:", "widget_class": StatusCell, "data_event_engine": tee,
                             "event_type": EVENT_TD_CONNECTED, "data_member": "", "visible": True},
            "td_login": {"label": "交易登录:", "widget_class": StatusCell, "data_event_engine": tee,
                         "event_type": EVENT_TD_LOGIN, "data_member": "", "visible": True},
            "qt_connected": {"label": "行情连接:", "widget_class": StatusCell, "data_event_engine": tee,
                             "event_type": EVENT_MD_CONNECTED, "data_member": "", "visible": True},
            "qt_login": {"label": "行情登录:", "widget_class": StatusCell, "data_event_engine": tee,
                         "event_type": EVENT_MD_LOGIN, "data_member": "", "visible": True},
            "available": {"label": "可用:", "widget_class": StatusCell, "data_event_engine": tee,
                          "event_type": EVENT_ACCOUNT, "data_member": "Available", "visible": True},
            "draw": {"label": "可取:", "widget_class": StatusCell, "data_event_engine": tee,
                     "event_type": EVENT_ACCOUNT, "data_member": "WithdrawQuota", "visible": True},
            "balance": {"label": "资产:", "widget_class": StatusCell, "data_event_engine": tee,
                        "event_type": EVENT_ACCOUNT, "data_member": "Balance", "visible": True},
            "fee": {"label": "手续费:", "widget_class": StatusCell, "data_event_engine": tee,
                    "event_type": EVENT_ACCOUNT, "data_member": "Commission", "visible": True},
            "margin": {"label": "保证金:", "widget_class": StatusCell, "data_event_engine": tee,
                       "event_type": EVENT_ACCOUNT, "data_member": "CurrMargin", "visible": True},
            "frozen_fee": {"label": "冻结费:", "widget_class": StatusCell, "data_event_engine": tee,
                           "event_type": EVENT_ACCOUNT, "data_member": "FrozenCommission",
                           "visible": True},
            "frozen_margin": {"label": "冻结金:", "widget_class": StatusCell, "data_event_engine": tee,
                              "event_type": EVENT_ACCOUNT, "data_member": "FrozenMargin", "visible": True},
            "close_profit": {"label": "平盈亏:", "widget_class": StatusCell, "data_event_engine": tee,
                             "event_type": EVENT_ACCOUNT, "data_member": "CloseProfit", "visible": True},
            "float_profit": {"label": "浮盈亏:", "widget_class": StatusCell, "data_event_engine": tee,
                             "event_type": EVENT_ACCOUNT, "data_member": "PositionProfit",
                             "visible": True},
            "log": {"label": "", "widget_class": StatusComboBox, "data_event_engine": tee,
                    "event_type": EVENT_LOG, "data_member": "msg", "visible": True}
        }
        for k, v in status_item.items():
            if not self.statusbar.findChild(v['widget_class'], k):
                item = v['widget_class'](k, data_event_engine=v['data_event_engine'],
                                         event_type=v['event_type'], label=v['label'],
                                         data_member=v['data_member'], visible=v['visible'])
                self.statusbar.addWidget(item)

    def init_dock(self):
        """"""
        # self.trading_widget, trading_dock = self.create_dock(TradingWidget, "交易",
        #                                                      QtCore.Qt.DockWidgetAreas.RightDockWidgetArea)
        tick_widget, tick_dock = self.create_dock(TickMonitor, "行情", QtCore.Qt.DockWidgetArea.LeftDockWidgetArea)

        # ta_widget, ta_dock = self.create_dock(TaWidget, "技术分析", QtCore.Qt.DockWidgetAreas.LeftDockWidgetArea, self)
        tick_widget.itemClicked.connect(self.tw_cell_clicked)

        # self.account_widget, account_dock = self.create_dock(AccountWidget, "账户", QtCore.Qt.BottomDockWidgetArea)
        self.account_widget = AccountWidget(me=self.main_engine, ee=self.event_engine)
        # self.account_widget.show()
        self.order_pad, order_dock = self.create_dock(OrderPad, "下单", QtCore.Qt.DockWidgetAreas.BottomDockWidgetArea,
                                                      parent=self)  # = (self.main_engine, self.event_engine, self)

        # self.tabifyDockWidget(tick_dock, ta_dock)
        # self.tabifyDockWidget(trading_dock, order_dock)
        # self.tabifyDockWidget(order_dock,tick_dock)
        self.tabifyDockWidget(order_dock, self.account_widget)
        self.account_widget.showMaximized()

        # self.instrument_changed.connect(self.trading_widget.on_instrument_id_changed)
        self.instrument_changed.connect(self.order_pad.on_instrument_changed)

        # self.account_widget.instrument_changed.connect(self.trading_widget.on_instrument_id_changed)
        self.account_widget.instrument_changed.connect(self.order_pad.on_instrument_changed)
        self.save_window_setting("default")

    def init_menu(self):
        """"""
        bar = self.menuBar()

        # System menu
        sys_menu = bar.addMenu("系统")

        gateway_names = self.main_engine.get_all_gateway_names()
        for name in gateway_names:
            func = partial(self.connect, name)
            self.add_menu_action(sys_menu, f"连接{name}", "connect.ico", func)

        sys_menu.addSeparator()

        self.add_menu_action(sys_menu, "退出", "exit.ico", self.close)

        # App menu
        app_menu = bar.addMenu("功能")

        all_apps = self.main_engine.get_all_apps()
        for app in all_apps:
            ui_module = import_module(app.app_module + ".ui")
            widget_class = getattr(ui_module, app.widget_name)

            func = partial(self.open_widget, widget_class, app.app_name)
            icon_path = str(app.app_path.joinpath("ui", app.icon_name))
            self.add_menu_action(
                app_menu, app.display_name, icon_path, func
            )
            self.add_toolbar_action(
                app.display_name, icon_path, func
            )

        # Global setting editor
        action = QtGui.QAction("配置", self)
        action.triggered.connect(self.edit_global_setting)
        bar.addAction(action)

        # Help menu
        help_menu = bar.addMenu("帮助")

        self.add_menu_action(
            help_menu,
            "查询合约",
            "contract.ico",
            partial(self.open_widget, ContractManager, "contract"),
        )
        self.add_toolbar_action(
            "查询合约",
            "contract.ico",
            partial(self.open_widget, ContractManager, "contract")
        )

        self.add_menu_action(
            help_menu,
            "代码编辑",
            "editor.ico",
            partial(self.open_widget, CodeEditor, "editor")
        )
        self.add_toolbar_action(
            "代码编辑",
            "editor.ico",
            partial(self.open_widget, CodeEditor, "editor")
        )

        self.add_menu_action(
            help_menu, "还原窗口", "restore.ico", self.restore_window_setting
        )

        self.add_menu_action(
            help_menu, "测试邮件", "email.ico", self.send_test_email
        )

        self.add_menu_action(
            help_menu, "社区论坛", "forum.ico", self.open_forum
        )
        self.add_toolbar_action(
            "社区论坛", "forum.ico", self.open_forum
        )

        self.add_menu_action(
            help_menu,
            "关于",
            "about.ico",
            partial(self.open_widget, AboutDialog, "about"),
        )

    def init_toolbar(self):
        """"""
        self.period_toolbar = QtWidgets.QToolBar(self)
        self.period_toolbar.setObjectName("周期")
        self.period_toolbar.setFloatable(True)
        self.period_toolbar.setMovable(True)

        # Set button size
        w = 40
        size = QtCore.QSize(w, w)
        self.period_toolbar.setIconSize(size)

        # Set button spacing
        self.period_toolbar.layout().setSpacing(10)
        for k, v in period_zh_en_dict.items():
            act = QtGui.QAction(k, self)
            act.triggered.connect(partial(self.period_toolbar_clicked, k))
            self.period_toolbar.addAction(act)

        self.addToolBar(QtCore.Qt.ToolBarAreas.LeftToolBarArea, self.period_toolbar)

        self.toolbar = QtWidgets.QToolBar(self)
        self.toolbar.setObjectName("工具栏")
        self.toolbar.setFloatable(True)
        self.toolbar.setMovable(True)

        # Set button size
        w = 40
        size = QtCore.QSize(w, w)
        self.toolbar.setIconSize(size)

        # Set button spacing
        self.toolbar.layout().setSpacing(10)

        self.addToolBar(QtCore.Qt.ToolBarAreas.LeftToolBarArea, self.toolbar)
        act = QtGui.QAction('连接', self)
        act.triggered.connect(partial(self.connect, 'CTP'))
        act.setShortcut('F12')
        self.toolbar.addAction(act)
        act = QtGui.QAction('Account', self)
        act.triggered.connect(self.show_account)
        act.setShortcut('F11')
        self.toolbar.addAction(act)

    def period_toolbar_clicked(self, period: str):
        self.period_changed_signal.emit(period)

    def show_trade_pad(self):
        if self.order_pad:
            self.order_pad.show()

    def show_account(self):
        if self.account_widget:
            self.account_widget.showMaximized()

    def add_menu_action(
            self,
            menu: QtWidgets.QMenu,
            action_name: str,
            icon_name: str,
            func: Callable,
    ):
        """"""
        icon = QtGui.QIcon(get_icon_path(__file__, icon_name))

        action = QtGui.QAction(action_name, self)
        action.triggered.connect(func)
        action.setIcon(icon)

        menu.addAction(action)

    def add_toolbar_action(
            self,
            action_name: str,
            icon_name: str,
            func: Callable,
    ):
        """"""
        icon = QtGui.QIcon(get_icon_path(__file__, icon_name))

        action = QtGui.QAction(action_name, self)
        action.triggered.connect(func)
        action.setIcon(icon)

        self.toolbar.addAction(action)

    def create_dock(
            self, widget_class: QtWidgets.QWidget, name: str, area: int, parent=None
    ):
        """
        Initialize a dock widget.
        """
        if parent:
            widget = widget_class(self.main_engine, self.event_engine, parent=parent)
        else:
            widget = widget_class(self.main_engine, self.event_engine)

        dock = QtWidgets.QDockWidget(name)
        dock.setWidget(widget)
        dock.setObjectName(name)
        # dock.setFeatures(
        #     QtWidgets.QDockWidget.DockWidgetFeatures.DockWidgetFloatable | QtWidgets.QDockWidget.DockWidgetFeatures.DockWidgetMovable)
        self.addDockWidget(area, dock)
        return widget, dock

    def connect(self, gateway_name: str):
        """
        Open connect dialog for gateway connection.
        """
        dialog = self.connect_dialogs.get(gateway_name, None)
        if not dialog:
            dialog = ConnectDialog(self.main_engine, gateway_name)

        dialog.exec()

    def closeEvent(self, event):
        """
        Call main engine close function before exit.
        """
        reply = QtWidgets.QMessageBox.question(
            self,
            "退出",
            "确认退出？",
            QtWidgets.QMessageBox.StandardButtons.Yes | QtWidgets.QMessageBox.StandardButtons.No,
            QtWidgets.QMessageBox.StandardButtons.No

        )

        if reply == QtWidgets.QMessageBox.StandardButtons.Yes:
            for widget in self.widgets.values():
                widget.close()
            if self.order_pad:
                self.order_pad.close()
            if self.account_widget:
                self.account_widget.close()
            self.save_window_setting("custom")

            self.main_engine.close()

            event.accept()
        else:
            event.ignore()

    def open_widget(self, widget_class: QtWidgets.QWidget, name: str):
        """
        Open contract manager.
        """
        widget = self.widgets.get(name, None)
        if not widget:
            widget = widget_class(self.main_engine, self.event_engine)
            self.widgets[name] = widget

        if isinstance(widget, QtWidgets.QDialog):
            widget.exec()
        else:
            widget.show()

    def save_window_setting(self, name: str):
        """
        Save current window size and state by trader path and setting name.
        """
        settings = QtCore.QSettings(self.window_title, name)
        settings.setValue("state", self.saveState())
        settings.setValue("geometry", self.saveGeometry())

    def load_window_setting(self, name: str):
        """
        Load previous window size and state by trader path and setting name.
        """
        settings = QtCore.QSettings(self.window_title, name)
        state = settings.value("state")
        geometry = settings.value("geometry")

        if isinstance(state, QtCore.QByteArray):
            self.restoreState(state)
            self.restoreGeometry(geometry)

    def restore_window_setting(self):
        """
        Restore window to default setting.
        """
        self.load_window_setting("default")
        self.showMaximized()

    def send_test_email(self):
        """
        Sending a test email.
        """
        self.main_engine.send_email("VN Trader", "testing")

    def open_forum(self):
        """
        """
        webbrowser.open("https://www.vnpy.com/forum/")

    def edit_global_setting(self):
        """
        """
        dialog = GlobalDialog()
        dialog.exec()

    def tw_cell_clicked(self, x):
        sender = self.sender()
        # print(x)
        # print(x.__dict__)
        if isinstance(sender, (TickMonitor, ActiveOrderMonitor, PositionMonitor)):
            symbol = x.__dict__['_data'].symbol
            if self.current_instrument_id != symbol:
                self.current_instrument_id = symbol
                self.instrument_changed.emit(self.current_instrument_id)
